package com.zzyl.nursing.job;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IAlertRuleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
@Slf4j
public class AlertJob {
    @Autowired
    private IAlertRuleService alertRuleService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private IAlertDataService alertDataService;
    @Autowired
    private DeviceMapper deviceMapper;

    public void alert() {
        long start = System.currentTimeMillis();
        log.info("开始执行定时任务......");
        /*1.查询所有可用预警规则，检查所有启用规则的预警触发
          2.从redis获取设备最新上报数据
          3.对设备上报数据进行过滤排除，过滤后的数据是某一大类产品所有的预警属性值，需要遍历
          4.进行时间段比较
          5.时间段符合再进行预警规则阈值判定
          6.通过阈值判定之后还需要进行持续周期与沉默周期判定，通过redis进行
          7.满足上诉所有条件进行预警信息表批量插入操作；查询接收人，根据老人或者设备类型构造不同的查询条件
         */
        //获取所有预警规则
        LambdaQueryWrapper<AlertRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AlertRule::getStatus, CacheConstants.STATUS_SUCCESS);
        List<AlertRule> alertRules = alertRuleService.list(queryWrapper);
        if (CollectionUtils.isEmpty(alertRules)) {
            log.info("没有生效中的预警规则");
            return;
        }
        for (AlertRule alertRule : alertRules) {
            log.info("开始处理设备{}的预警规则", alertRule.getAlertRuleName());
            executRules(alertRule);
        }
        log.info("预警任务执行完毕，耗时：{}", System.currentTimeMillis() - start);
    }

    /**
     * 执行规则
     */
    private void executRules(AlertRule rule) {
        //获取设备上报数据
        List<Object> values = redisTemplate.opsForHash().values(CacheConstants.IOT_NEW_DATA);
        if (CollectionUtils.isEmpty(values)) {
            log.info("无设备数据");
            return;
        }
        List<DeviceData> deviceDataList = new ArrayList<>();
        for (Object value : values) {
            deviceDataList = JSONUtil.toList(value.toString(), DeviceData.class);
        }
        //过滤排除非预警数据
        List<DeviceData> dataList = deviceDataList.stream().
                filter(d -> d.getFunctionId().equals(rule.getFunctionId())
                        && d.getIotId().equals(rule.getIotId())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(dataList)) {
            log.info("没有找到预警数据");
            return;
        }
        for (DeviceData data : dataList) {
            log.info("开始处理预警数据：{}", data);
            //比较时间段
            getDataList(data, rule);
        }
    }

    /**
     * 获取数据列表
     */
    private void getDataList(DeviceData data, AlertRule rule) {
        log.info("需要处理的预警数据：{}", rule.getAlertRuleName());
        String period = rule.getAlertEffectivePeriod();
        String[] split = period.split(CacheConstants.STRING_SYMBOL);

        LocalTime start = LocalTime.parse(split[0]);
        LocalTime end = LocalTime.parse(split[1]);
        LocalTime alarmTime = data.getAlarmTime().toLocalTime();
        //判断时间段
        if (alarmTime.isBefore(start) && alarmTime.isAfter(end)) {
            log.info("不在时间段内，无需预警");
            return;
        }
        log.info("上报时间{}在预警时间段{},{}内，开始预警", alarmTime, start, end);

        //进行预警规则阈值判定
        Boolean result = checkTime(data, rule);

        if (!result) {
            log.info("数据不满足预警条件");
            return;
        }
        String key = CacheConstants.REDIS_ALERT_COUNT + rule.getId()
                + CacheConstants.REDIS_ALERT_SYMBOL + data.getIotId();
        Object res = redisTemplate.opsForValue().get(key);
        //满足预警条件，进行持续周期和沉默周期判断
        int count = ObjectUtils.isEmpty(res) ? CacheConstants.NUMBER_ONE
                : (Integer.parseInt(res.toString())
                + CacheConstants.NUMBER_ONE);
        //持续周期小于阈值，进行计数
        if (count < rule.getDuration()) {
            log.info("预警次数{}小于执行周期{}", count, rule.getDuration());
            redisTemplate.opsForValue().set(key, count);
            return;
        }
        //持续周期大于阈值，进行沉默周期判断
        String keySilent = CacheConstants.REDIS_ALERT_SILENT_KEY
                + rule.getId() + CacheConstants.REDIS_ALERT_SYMBOL
                + data.getIotId();
        Object resSilent = redisTemplate.opsForValue().get(keySilent);
        if (!ObjectUtils.isEmpty(resSilent)) {
            log.info("在沉默周期内{}，清空计数{}", resSilent, count);
            redisTemplate.delete(key);
            return;
        }
        //清空计数，进入沉默周期
        redisTemplate.delete(key);
        redisTemplate.opsForValue().set(keySilent, CacheConstants.REDIS_ALERT_SILENT_VALUE,
                rule.getAlertSilentPeriod(), TimeUnit.MINUTES);

        //满足上诉所有条件，进行批量插入数据库
        saveBatchAlertData(data, rule);
    }

    /**
     * 批量插入数据库
     */
    private void saveBatchAlertData(DeviceData data, AlertRule rule) {
        //记录到数据库，查询接收人（可能多个）
        List<Integer> userIds = getUserIds(data, rule);
        for (Integer userId : userIds) {
            AlertData alertData = new AlertData();
            BeanUtils.copyProperties(data, alertData);
            alertData.setId(null);
            alertData.setAlertRuleId(rule.getId());
            //报警原因
            String alertReason = rule.getFunctionName() + rule.getOperator() + rule.getValue()
                    + rule.getDuration() + rule.getAlertEffectivePeriod();
            alertData.setAlertReason(alertReason);
            alertData.setStatus(CacheConstants.STATUS_FAIL);
            alertData.setType(rule.getAlertDataType());
            alertData.setUserId(userId);
            alertDataService.save(alertData);
        }
    }

    /**
     * 获取用户ID
     */
    private List<Integer> getUserIds(DeviceData data, AlertRule rule) {
        List<Integer> userIds = new ArrayList<>();
        // 老人数据报警
        if (Objects.equals(rule.getAlertDataType(), CacheConstants.NUMBER_ZERO)) {
            if (Objects.equals(data.getLocationType(), CacheConstants.NUMBER_ZERO)) {
                userIds = deviceMapper.getNursingIdsByIotIdAndElderId(data.getIotId());

            } else if (Objects.equals(data.getLocationType(), CacheConstants.NUMBER_ONE)
                    && Objects.equals(data.getPhysicalLocationType(), CacheConstants.NUMBER_TWO)) {
                userIds = deviceMapper.getNursingIdsByIotIdAndBedId(data.getIotId());
            }
        }
        //todo
        else {
        }
        log.info("预警信息需要通知的人员id{}", userIds);
        return userIds;
    }

    /**
     * 检测时间段内数据是否满足预警条件
     */
    private Boolean checkTime(DeviceData data, AlertRule rule) {
        log.info("进行时间段内数据判断");
        Double dataValue = Double.parseDouble(data.getDataValue());
        Double ruleValue = rule.getValue();
        if (CacheConstants.STRING_EQUAL_THAN.equals(rule.getOperator())) {
            if (dataValue >= ruleValue) {
                log.info("数据{}满足阈值条件{}", dataValue, ruleValue);
                return true;
            }

        } else if (CacheConstants.STRING_LESS_THAN.equals(rule.getOperator())) {
            if (dataValue < ruleValue) {
                log.info("预警数据{}满足阈值条件{}", dataValue, ruleValue);
                return true;
            }
        }
        return false;
    }
}
